/**
 * @file ili9486.c
 * @author cangyu (sky.kirto@qq.com)
 * @brief
 * @version 0.1
 * @date 2024-09-05
 *
 * @copyright Copyright (c) 2024, CorAL. All rights reserved.
 *
 */

/* ==================== [Includes] ========================================== */

#include "../lcd_drive_internal.h"

/* ==================== [Defines] =========================================== */

#define ILI9486_RESOLUTION_HOR 320
#define ILI9486_RESOLUTION_VER 480

#define TAG "ILI9486"

/* ==================== [Typedefs] ========================================== */

typedef struct _ili9486_cmd_t {
    uint8_t cmd;
    uint8_t data[15];
    uint32_t len;
} ili9486_cmd_t;

/* ==================== [Static Prototypes] ================================= */

static xf_err_t ili9486_init(lcd_t self);
static xf_err_t ili9486_draw_bitmap(lcd_t self, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *bitmap);
static xf_err_t ili9486_set_window(lcd_t self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);

/* ==================== [Static Variables] ================================== */

const static lcd_drive_out_t ili9486_out = {
    .res_hor = ILI9486_RESOLUTION_HOR,
    .res_ver = ILI9486_RESOLUTION_VER,
    .init = ili9486_init,
    .draw_bitmap = ili9486_draw_bitmap
};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

void lcd_ili9486_vfunc_register(void)
{
    lcd_controller_driver_reg(SCR_CONTROLLER_ili9486, &ili9486_out);
}

/* ==================== [Static Functions] ================================== */

static xf_err_t ili9486_init(lcd_t self)
{
    const static ili9486_cmd_t init[] = {
        {0xb0, {0x00}, 1},
        {0x3A, {0x55}, 1},
        {0xE0, {0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00}, 15},
        {0xE1, {0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00}, 15},
        {0x38, {0x00}, 0},
        {0xB1, {0xB0, 0x11}, 2},
        {0xC2, {0x55}, 1},
        {0xB4, {0x02}, 1},
        {0xB6, {0x02, 0x22, 0x3B}, 3},
        {0x11, {0x00}, 0},
        {0x29, {0x00}, 0},
    };
    xf_err_t ret = XF_OK;
    ret |= LCD_RESET(self);
    for (int i = 0; i < sizeof(init) / sizeof(init[0]); i++) {
        ret |= LCD_WRITE_CMD(self, &init[i].cmd, 1);
        if (init[i].len != 0) {
            ret |= LCD_WRITE_DATA(self, init[i].data, init[i].len);
        }
    }
    ret |= LCD_BACKLIGHT(self, 1);

    XF_LOGI(TAG, "ili9486 init ret:%d", ret);

    return ret;
}

static xf_err_t ili9486_draw_bitmap(lcd_t self, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *bitmap)
{
    xf_err_t ret = XF_OK;

    ret |= ili9486_set_window(self, x, y, x + w - 1, y + h - 1);
    uint32_t len = w * h;
    XF_LOGI(TAG, "ili9486_draw_bitmap len:%d", len);
    ret |= LCD_WRITE_DATA(self, bitmap, len * 2);
    XF_LOGI(TAG, "ili9486_draw_bitmap");
    return 0;
}


static xf_err_t ili9486_set_window(lcd_t self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    xf_err_t err = XF_OK;
    lcd_drive_offset(self, &x0, &y0, &x1, &y1);
    static ili9486_cmd_t init[] = {
        {0x2A, {0}, 4},
        {0x2B, {0}, 4},
        {0x2C, {0}, 0},
    };
    init[0].data[0] = (x0 >> 8) & 0xff;
    init[0].data[1] = x0 & 0xff;
    init[0].data[2] = (x1 >> 8) & 0xff;
    init[0].data[3] = x1 & 0xff;
    init[1].data[0] = (y0 >> 8) & 0xff;
    init[1].data[1] = y0 & 0xff;
    init[1].data[2] = (y1 >> 8) & 0xff;
    init[1].data[3] = y1 & 0xff;

    for (int i = 0; i < sizeof(init) / sizeof(init[0]); i++) {
        err |= LCD_WRITE_CMD(self, &init[i].cmd, 1);
        if (init[i].len != 0) {
            err |= LCD_WRITE_DATA(self, init[i].data, init[i].len);
        }
    }

    return err;
}
